package estring

import (
	"bytes"
	"fmt"
	"mime/multipart"
	"strconv"
	"strings"
)

const hextable = "0123456789ABCDEF"

// EncodeHexWithSpace 代空格编码16进制
func EncodeHexWithSpace(dst, src []byte) int {
	for i, v := range src {
		dst[i*3] = hextable[v>>4]
		dst[i*3+1] = hextable[v&0x0f]
		dst[i*3+2] = ' '
	}

	return len(src) * 3
}

// EncodeHexWithSpaceLen 带空格编码16进制长度
func EncodeHexWithSpaceLen(l int) int {
	return l * 3
}

// IsDigit 是否是数字
func IsDigit(b byte) bool {
	return '0' <= b && b <= '9'
}

// IsAlpha 是否是字母
func IsAlpha(b byte) bool {
	return ('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z')
}

// ArrayIntToString int 转为 string
func ArrayIntToString(vals []int) []string {
	ss := make([]string, len(vals))
	for i := range vals {
		ss[i] = strconv.Itoa(vals[i])
	}
	return ss
}

// ToString 格式化浮点数
func ToString(f float64) (s string) {
	return strconv.FormatFloat(f, 'f', -1, 64)
}

// GetIndex 获取key在行中的索引号
func GetIndex(line string, key string) int {
	ss := strings.Split(line, ",")
	for i, s := range ss {
		s = strings.TrimSpace(s)
		if s == key {
			return i
		}
	}
	return -1
}

// String 去掉为0之后的字符
func String(data []byte) string {
	l := len(data)
	for i := 0; i < l; i++ {
		if data[i] == 0 {
			return string(data[:i])
		}
	}
	return string(data)
}

// IsCrLf 是否是回车换行
func IsCrLf(r rune) bool {
	if r == '\r' || r == '\n' {
		return true
	}
	return false
}

// StringInArray 字符串是否在一个数组中
func StringInArray(str string, stringArray []string) bool {
	str = strings.ToUpper(str)
	for i := range stringArray {
		if str == stringArray[i] {
			return true
		}
	}
	return false
}

// CompareNoCase 不区分大小写的字符串比较
func CompareNoCase(s1, s2 string) bool {
	return strings.EqualFold(s1, s2)
}

// ComparePath 路径比较
func ComparePath(s1, s2 string) bool {
	s1 = strings.Replace(s1, "\\", "/", -1)
	s2 = strings.Replace(s2, "\\", "/", -1)
	return strings.EqualFold(s1, s2)
}

// FindString 查找字符串
func FindString(line, key string) string {
	ss := strings.Split(line, ";")
	for _, s := range ss {
		ss := strings.SplitN(s, "=", 2)
		if len(ss) == 2 {
			if strings.TrimSpace(ss[0]) == key {
				return strings.TrimSpace(ss[1])
			}
		}
	}
	return ""
}

// FormatSize 格式化大小
func FormatSize(size int64) string {
	msg := fmt.Sprintf("%d Bytes", size)

	switch {
	case size > 1024:
		msg = fmt.Sprintf("%.2f KB", float64(size)/1024.0)
	case size > (1024 * 1024):
		msg = fmt.Sprintf("%.2f KB", float64(size)/1024.0/1024.0)
	}
	return msg
}

// GetFileCode 获取文件 编码
// 返回空置认为是 utf8 编码
func GetFileCode(file multipart.File) (code string) {
	//读取16k的数据
	data := make([]byte, 8192)
	n, _ := file.Read(data)
	data = data[:n]
	if n == 0 {
		return
	}

	code, offset := GetCode(data)

	file.Seek(offset, 0)
	return
}

func isGBK(data []byte) bool {
	length := len(data) - 1
	var i int = 0
	for i < length {
		if data[i] <= 0x7f {
			//编码0~127,只有一个字节的编码，兼容ASCII码
			i++
			continue
		} else {
			//大于127的使用双字节编码，落在gbk编码范围内的字符
			if data[i] >= 0x81 &&
				data[i] <= 0xfe &&
				data[i+1] >= 0x40 &&
				data[i+1] <= 0xfe &&
				data[i+1] != 0xf7 {
				i += 2
				//有一个是gbk，就认为是gbk
				return true
			}
			i++
		}
	}
	return false
}

func preNUm(data byte) int {
	var mask byte = 0x80
	var num int = 0
	//8bit中首个0bit前有多少个1bits
	for i := 0; i < 8; i++ {
		if (data & mask) == mask {
			num++
			mask = mask >> 1
		} else {
			break
		}
	}
	return num
}
func isUtf8(data []byte) bool {
	i := 0
	for i < len(data) {
		if (data[i] & 0x80) == 0x00 {
			// 0XXX_XXXX
			i++
			continue
		} else if num := preNUm(data[i]); num > 2 {
			// 110X_XXXX 10XX_XXXX
			// 1110_XXXX 10XX_XXXX 10XX_XXXX
			// 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
			// 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
			// 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
			// preNUm() 返回首个字节的8个bits中首个0bit前面1bit的个数，该数量也是该字符所使用的字节数
			i++
			for j := 0; j < num-1 && i < len(data); j++ {
				//判断后面的 num - 1 个字节是不是都是10开头
				if (data[i] & 0xc0) != 0x80 {
					return false
				}
				i++
			}
		} else {
			//其他情况说明不是utf-8
			return false
		}
	}
	return true
}

// GetCode 获取文字编码
// 默认 utf8编码
func GetCode(data []byte) (code string, offset int64) {
	var utf8Bom = []byte{0xEF, 0xBB, 0xBF}
	switch {
	case bytes.HasPrefix(data, utf8Bom): //len(data) >= 3 && data[0] == 0xef && data[1] == 0xbb && data[2] == 0xbf:
		offset = 3
	default:
		//必须先判断utf8
		if isUtf8(data) {
			return
		} else if isGBK(data) {
			code = "GBK"
			return
		}
	}
	return
}
